package be.kuleuven.thesis.status;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import be.kuleuven.thesis.framework.Filter;
import be.kuleuven.thesis.framework.Song;
import be.kuleuven.thesis.framework.SongDatabase;

import android.os.AsyncTask;

public class PlaylistGenerator {
	private static PlaylistGenerator instance = new PlaylistGenerator();
	private boolean on;
	private Set<Song> likedSongs = new HashSet<Song>();
	private Set<Song> dislikedSongs = new HashSet<Song>();
	private int nbOfSongsToGenerate = 1;
	private Player player;
	private Filter filter = new Filter();
	private StatusListener listener;
	private boolean mutex = false;
	
	private PlaylistGenerator() {
		player = Player.getInstance();
	}
	
	public static PlaylistGenerator getInstance() {
		return instance;
	}
	
	public void reset() {
		likedSongs.clear();
		dislikedSongs.clear();
	}
	
	public boolean isRadioOn() {
		return on;
	}

	public void turnRadioOn() {
		on = true;
		checkGeneration();
		listener.onRadioSwitched();
	}
	
	public void turnRadioOff() {
		on = false;
		listener.onRadioSwitched();
	}

	public int getNbOfSongsToGenerate() {
		return nbOfSongsToGenerate;
	}
	
	public void incrementNbOfSongsToGenerate() {
		if(nbOfSongsToGenerate <= 10)
			nbOfSongsToGenerate++;
		checkGeneration();
	}
	
	public void decrementNbOfSongsToGenerate() {
		if(nbOfSongsToGenerate > 1)
			nbOfSongsToGenerate--;
		checkGeneration();
	}
	
	public void likeSong(Song song) {
		if(isDisliked(song))
			dislikedSongs.remove(song);
		likedSongs.add(song);
		checkGeneration();
	}
	
	public boolean isLiked(Song song) {
		return likedSongs.contains(song);
	}
	
	public void unlikeSong(Song song) {
		likedSongs.remove(song);
	}
	
	public void dislikeSong(Song song) {
		if(isLiked(song))
			unlikeSong(song);
		dislikedSongs.add(song);
	}
	
	public boolean isDisliked(Song song) {
		return dislikedSongs.contains(song);
	}
	
	public Set<Song> getLikedSongs() {
		return likedSongs;
	}
	
	private int getUnlikedSongsAfterPlay() {
		int unlikedSongs = 0;
		for(int i = player.getCurrentIndex()+1; i <= player.getCurrentPlaylist().size();i++) {
			if(!isLiked(player.getCurrentPlaylist().get(i-1)))
				unlikedSongs++;
		}
		return unlikedSongs;
	}
	
	public void generateSongs(Set<Song> possibilities, int number) {
		Iterator<Song> it = possibilities.iterator();
		for(int i = 0; i< number; i++) {
			if(it.hasNext()) {
				Song s = it.next();
				if(!player.getCurrentPlaylist().contains(s) && s.satisfiesFilter(filter)) {
					player.addGeneratedSong(s);
				} else i--;
			} else {
				System.out.println("Songs zijn op!");
				break;
			}
		}
//		if(currentPlaylistView != null)
//			currentPlaylistView.getSongAdapter().notifyDataSetChanged();
	}
	
	public void checkGeneration() {
		if(!isRadioOn())
			return;
		if(getUnlikedSongsAfterPlay() < getNbOfSongsToGenerate() && !isGenerating()) {
			acquireMutex();
			new GeneratedSongsTask().execute();
		}
	}
	
	public boolean isGenerating() {
		return mutex;
	}
	
	private void acquireMutex() {
		mutex = true;
	}
	
	private void releaseMutex() {
		mutex = false;
		checkGeneration();
	}
	
	private class GeneratedSongsTask extends AsyncTask<Void, Song, Void> {
		Set<Song> possibilities;
		int number = getNbOfSongsToGenerate()-getUnlikedSongsAfterPlay();
		// automatically done on worker thread (separate from UI thread)
		protected Void doInBackground(Void ...unused) {
			possibilities = SongDatabase.generateSongs(getLikedSongs(), dislikedSongs, null, null);
			return null;
		}

		// can use UI thread here
		@Override
		protected void onPostExecute(final Void unused) {
			generateSongs(possibilities, number);
			releaseMutex();
		}
	}

	public Filter getFilter() {
		return filter;
	}

	public void setFilter(Filter filter) {
		this.filter = filter;
	}
	
	public void applyFilter() {
		for(int i = player.getPlaylistSize(); i >= 1;i--) {
			Song songI = player.getCurrentPlaylist().get(i-1);
			if(!likedSongs.contains(songI) && songI.satisfiesFilter(filter));
				Player.getInstance().removeSong(i);
		}
	}
	
	public StatusListener getStatusListener() {
		return listener;
	}

	public void setStatusListener(StatusListener listener) {
		this.listener = listener;
	}
}
